#ifndef CSUPBR_ACCEL_H
#define CSUPBR_ACCEL_H

#include <vector>
#include <memory>

#include "primitive.h"

namespace csupbr {
    struct Accel : Primitive {
        BBox3f getBBox() const override { return BBox3f(); }
        virtual void add_primitive(std::shared_ptr<Primitive> primitive) = 0;
        virtual void add_primitive(std::vector<std::shared_ptr<Primitive>> primitive) = 0;
        virtual void build() {}
    };

    struct List : Accel {
        void add_primitive(std::shared_ptr<Primitive> primitive) override {
            primitives.push_back(primitive);
        }

        void add_primitive(std::vector<std::shared_ptr<Primitive>> primitive) override {
            primitives.insert(primitives.begin(), primitive.begin(), primitive.end());
        }

        bool intersect(const Ray &ray, Intersection &isct) const override {
            bool is_intersected = false;
            for (auto primitive : primitives) {
                if (primitive->intersect(ray, isct)) {
                    is_intersected = true;
                }
            }
            return is_intersected;
        }

        bool intersectP(const Ray &ray, bool blend_test) const override {
            bool is_intersected = false;
            for (auto primitive : primitives) {
                if (primitive->intersectP(ray, blend_test)) {
                    is_intersected = true;
                }
            }
            return is_intersected;
        }

    private:
        std::vector<std::shared_ptr<Primitive>> primitives;
    };
    
	struct BVHBuildNode;
	struct BVHPrimitiveInfo;

	struct LinearBVHNode {
		BBox3f bbox;
		union {
			uint32_t primitiveOffset;
			uint32_t secondChildOffset;
		};
		uint8_t nPrimitives;
		uint8_t axis;
		uint8_t pad[2];
	};

    struct BVHTree : Accel {
        void add_primitive(std::shared_ptr<Primitive> primitive) override {
            primitives.push_back(primitive);
        }
        
        void add_primitive(std::vector<std::shared_ptr<Primitive>> primitive) override {
            primitives.insert(primitives.begin(), primitive.begin(), primitive.end());
        }

        void build() override;
        bool intersect(const Ray &ray, Intersection &isct) const override;
        bool intersectP(const Ray &ray, bool blend_test) const override;
        
    private:
    
		BVHBuildNode* recursiveBuild(std::vector<BVHPrimitiveInfo>& primitiveInfo,
			int start, int end, int& totalNodes, std::vector<std::shared_ptr<Primitive>>& orderedPrims);

		int flattenTree(BVHBuildNode* node, int& offset);

        std::vector<std::shared_ptr<Primitive>> primitives;
        std::vector<LinearBVHNode> lbnodes;
    };

}

#endif // !CSUPBR_ACCEL_H